package service

import (
	"admin100/app/dao"
	"admin100/app/dao/commondao"
	"admin100/app/model"
	"admin100/app/service/centerservice"
	"admin100/app/service/gameservice"
	"admin100/library/define"
	"admin100/library/define/defErr"
	"admin100/library/utils"
	"fmt"
	"sort"
	"strings"

	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gconv"
)

var Remain = &remainService{}

type remainService struct{}

func (s *remainService) FetchRemainTotalList(req *model.TotalRemainReq) ([]*model.RemainTotal, int) {
	var channelSlice = utils.TrimSplitStr(req.ChannelList)
	var (
		days = (req.EndTime-req.StartTime)/86400 + 1
		data = make([]*model.RemainTotal, 0, days)
	)

	// theTime := req.StartTime + (i-1)*86400
	m := dao.RemainTotal.WhereValNotEmpty(g.Map{
		dao.RemainTotal.Columns.PlatformId:                req.PlatformId,
		dao.RemainTotal.Columns.Time + " between ? and ?": g.Slice{req.StartTime, req.EndTime},
		dao.RemainTotal.Columns.ServerId:                  req.ServerId,
		dao.RemainTotal.Columns.Channel:                   channelSlice,
	})
	m1 := m.Clone()
	tmpData, err := m.FindAll()
	defErr.SelectRetPanic(err, tmpData, define.DataOptFail)

	total, err := m1.Count()
	if err != nil {
		defErr.Panic(define.DataOptFail, err.Error())
	}

	dailysData, err := dao.DailyStatistics.WhereValNotEmpty(g.Map{
		dao.DailyStatistics.Columns.PlatformId:                req.PlatformId,
		dao.DailyStatistics.Columns.Time + " between ? and ?": g.Slice{req.StartTime, req.EndTime},
		dao.DailyStatistics.Columns.ServerId:                  req.ServerId,
		dao.DailyStatistics.Columns.Channel:                   channelSlice,
	}).FindAll()
	if err != nil {
		defErr.Panic(define.DataOptFail, err.Error())
	}

	var dailyTmpData = make(map[uint]*model.DailyStatistics)
	for _, v := range dailysData {
		dailyTmpData[uint(v.Time)] = v
	}

	for _, e := range tmpData {

		if e == nil {
			continue
		}

		tmpE := &model.RemainTotal{
			PlatformId: req.PlatformId,
			ServerId:   req.ServerId,
			Time:       uint(e.Time),
		}

		if daily, ok := dailyTmpData[e.Time]; ok {
			tmpE.CreateRole += daily.CreateRoleCount
			tmpE.RegisterRole += daily.TotalRegisterCount
		} else {
			tmpE.CreateRole += e.CreateRole
			tmpE.RegisterRole += e.RegisterRole
		}

		tmpE.Remain2 += e.Remain2
		tmpE.Remain3 += e.Remain3
		tmpE.Remain4 += e.Remain4
		tmpE.Remain7 += e.Remain7
		tmpE.Remain14 += e.Remain14
		tmpE.Remain30 += e.Remain30

		data = append(data, tmpE)
	}
	for _, e := range data {
		if e.CreateRole > 0 {
			e.Remain2 = int(float32(e.Remain2) / float32(e.CreateRole) * 10000)
		} else {
			e.Remain2 = 0
		}
		if e.CreateRole > 0 {
			e.Remain3 = int(float32(e.Remain3) / float32(e.CreateRole) * 10000)
		} else {
			e.Remain3 = 0
		}
		if e.CreateRole > 0 {
			e.Remain4 = int(float32(e.Remain4) / float32(e.CreateRole) * 10000)
		} else {
			e.Remain4 = 0
		}
		if e.CreateRole > 0 {
			e.Remain7 = int(float32(e.Remain7) / float32(e.CreateRole) * 10000)
		} else {
			e.Remain7 = 0
		}
		if e.CreateRole > 0 {
			e.Remain14 = int(float32(e.Remain14) / float32(e.CreateRole) * 10000)
		} else {
			e.Remain14 = 0
		}
		if e.CreateRole > 0 {
			e.Remain30 = int(float32(e.Remain30) / float32(e.CreateRole) * 10000)
		} else {
			e.Remain30 = 0
		}
	}
	return data, total
}

func (s *remainService) FetchRemainChargeList(req *model.ChargeRemainReq) []*model.RemainCharge {
	var channelSlice = utils.TrimSplitStr(req.ChannelList)
	var (
		days = (req.EndTime-req.StartTime)/86400 + 1
		data = make([]*model.RemainCharge, 0, days)
	)
	charges, err := dao.RemainCharge.WhereValNotEmpty(g.Map{
		dao.RemainCharge.Columns.PlatformId:                req.PlatformId,
		dao.RemainCharge.Columns.Time + " between ? and ?": g.Slice{req.StartTime, req.EndTime},
		dao.RemainCharge.Columns.ServerId:                  req.ServerId,
		dao.RemainCharge.Columns.Channel:                   channelSlice,
	}).FindAll()
	defErr.SelectRetPanic(err, charges, define.DataOptFail)

	for _, e := range charges {

		if e == nil {
			continue
		}

		tmpE := &model.RemainCharge{
			PlatformId: req.PlatformId,
			ServerId:   req.ServerId,
			Time:       e.Time,
		}
		tmpE.ChargeNum += e.ChargeNum
		tmpE.Remain2 += e.Remain2
		tmpE.Remain3 += e.Remain3
		tmpE.Remain4 += e.Remain4
		tmpE.Remain7 += e.Remain7
		tmpE.Remain14 += e.Remain14
		tmpE.Remain30 += e.Remain30
		data = append(data, tmpE)

	}
	for _, e := range data {
		if e.ChargeNum > 0 {
			e.Remain2 = int(float32(e.Remain2) / float32(e.ChargeNum) * 10000)
		} else {
			e.Remain2 = 0
		}
		if e.ChargeNum > 0 {
			e.Remain3 = int(float32(e.Remain3) / float32(e.ChargeNum) * 10000)
		} else {
			e.Remain3 = 0
		}
		if e.ChargeNum > 0 {
			e.Remain4 = int(float32(e.Remain4) / float32(e.ChargeNum) * 10000)
		} else {
			e.Remain4 = 0
		}
		if e.ChargeNum > 0 {
			e.Remain7 = int(float32(e.Remain7) / float32(e.ChargeNum) * 10000)
		} else {
			e.Remain7 = 0
		}
		if e.ChargeNum > 0 {
			e.Remain14 = int(float32(e.Remain14) / float32(e.ChargeNum) * 10000)
		} else {
			e.Remain14 = 0
		}
		if e.ChargeNum > 0 {
			e.Remain30 = int(float32(e.Remain30) / float32(e.ChargeNum) * 10000)
		} else {
			e.Remain30 = 0
		}

	}
	return data
}

func (s *remainService) FetchActiveList(req *model.ActiveRemainReq) (int, []*model.RemainActive) {

	m := dao.RemainActive.WhereValNotEmpty(g.Map{
		dao.RemainActive.Columns.PlatformId: req.PlatformId,
		dao.RemainActive.Columns.ServerId:   req.ServerId,
		dao.RemainActive.Columns.Channel:    utils.TrimSplitStr(req.ChannelList),
	})
	if req.StartTime > 0 {
		m = m.Where(dao.RemainActive.Columns.Time+" between ? and ?", req.StartTime, req.EndTime)
	}
	tmpModel := m.Clone()
	data, mErr := m.Page(req.Page, req.Nums).FindAll()

	defErr.IfPanic(mErr, define.DataOptFail)
	for _, v := range data {
		daily, dErr := dao.DailyStatistics.WhereValNotEmpty(g.Map{
			dao.DailyStatistics.Columns.PlatformId: v.PlatformId,
			dao.DailyStatistics.Columns.Time:       v.Time,
			dao.DailyStatistics.Columns.ServerId:   v.ServerId,
			dao.DailyStatistics.Columns.Channel:    utils.TrimSplitStr(req.ChannelList),
		}).FindOne()
		defErr.IfPanic(dErr, define.DataOptFail)
		if daily == nil {
			continue
		}
		v.CreateRole = daily.CreateRoleCount
		v.RegisterRole = daily.RegisterCount
	}

	// fetch page info
	total, err := tmpModel.Count()
	defErr.IfPanic(err, define.DataOptFail)

	return total, data
}

type RemainLevel struct {
	Level      int     `json:"level"`
	Count      int     `json:"count"`
	LeaveCount int     `json:"leaveCount"`
	Rate       float32 `json:"rate"`
	IsOnline   int     `json:"is_online"`
}

// 获取等级分布
func (s *remainService) FetchRemainLevel(platformId string, serverId string, channelList []string, startTime int, endTime int, isChargePlayer int) []*RemainLevel {
	_, gs := centerservice.CGameServer.GetGameServerList(platformId, []string{serverId}, true)
	gameDb := commondao.DB.NewCustomGdb(gs.Node)
	if gs == nil || gameDb == nil {
		return nil
	}

	var (
		data       = make([]*RemainLevel, 0)
		mapData    = make(map[int]*RemainLevel)
		sql        = ""
		joinStr    = ""
		whereArray = make([]string, 0)
	)

	// todo 后期改成model模式书写
	// 0 全部玩家  1 充值过的玩家   2 没充值的玩家
	if isChargePlayer == 1 {
		joinStr = " inner join (select DISTINCT(player_id) as charge_player_id from player_charge_shop) as charge_player on player_id = charge_player.charge_player_id "
	}
	if isChargePlayer == 2 {
		whereArray = append(whereArray, " charge_player.charge_player_id IS NULL")
		joinStr = " left join (select DISTINCT(player_id) as charge_player_id from player_charge_shop) as charge_player on player_id = charge_player.charge_player_id "
	}
	cStr := strings.Join(channelList, "','")
	whereArray = append(whereArray, fmt.Sprintf(" player.channel in ('%s') ", cStr))

	if startTime > 0 {
		whereArray = append(whereArray, fmt.Sprintf(" reg_time BETWEEN %d AND %d ", startTime, endTime))
	}

	whereParam := strings.Join(whereArray, " and ")
	if whereParam != "" {
		whereParam = " where " + whereParam
	}

	sql = fmt.Sprintf(
		`SELECT player.id, player.is_online, player_data.level FROM (player left join player_data on player.id = player_data.player_id) %s %s `, joinStr, whereParam)
	fmt.Println(sql)
	r, err := gameDb.GetAll(sql)
	defErr.IfPanic(err, define.DataOptFail)

	var res = make([]*RemainLevel, 0, len(r))
	for _, v := range r {
		var remainLevel = &RemainLevel{}
		if err = gconv.Struct(v, remainLevel); err != nil {
			defErr.IfPanic(err, define.Fail)
		}
		res = append(res, remainLevel)

	}
	for _, e := range res {
		if r, ok := mapData[e.Level]; ok {
			if e.IsOnline == 1 {
				r.Count++
			} else {
				r.Count++
				r.LeaveCount++
			}
		} else {
			if e.IsOnline == 1 {
				mapData[e.Level] = &RemainLevel{
					Level:      e.Level,
					Count:      1,
					LeaveCount: 0,
				}
			} else {
				mapData[e.Level] = &RemainLevel{
					Level:      e.Level,
					Count:      1,
					LeaveCount: 1,
				}
			}
		}

	}

	totalCreateRole := gameservice.Account.GetTotalCreateRoleCountByChannelList(gs.Node, gs.Sid, channelList)
	var keys []int
	for k, _ := range mapData {
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, id := range keys {
		e := mapData[id]
		e.Rate = float32(e.LeaveCount) / float32(totalCreateRole) * 100
		data = append(data, e)
	}
	return data
}

type RemainTask struct {
	TaskId     int     `json:"taskId"`
	Count      int     `json:"count"`
	LeaveCount int     `json:"leaveCount"`
	Rate       float32 `json:"rate"`
	Status     int     `json:"status"`
	IsOnline   int     `json:"isOnline"`
}

// 获取任务分布
func (s *remainService) FetchTaskRemain(platformId string, serverId string, channelList []string, isChargePlayer int) []*RemainTask {
	_, gs := centerservice.CGameServer.GetGameServerList(platformId, []string{serverId}, true)
	gameDb := commondao.DB.NewCustomGdb(gs.Node)
	if gs == nil || gameDb == nil {
		return nil
	}
	var (
		data       = make([]*RemainTask, 0)
		mapData    = make(map[int]*RemainTask)
		sql        = ""
		joinStr    = ""
		whereArray = make([]string, 0)
	)

	// todo 后期改成model模式书写
	// 0 全部玩家  1 充值过的玩家   2 没充值的玩家
	if isChargePlayer == 1 {
		joinStr = " inner join (select DISTINCT(player_charge_shop.player_id) as charge_player_id from player_charge_shop) as charge_player on player.id = charge_player.charge_player_id "
	}
	if isChargePlayer == 2 {
		whereArray = append(whereArray, " charge_player.charge_player_id IS NULL")
		joinStr = " left join (select DISTINCT(player_charge_shop.player_id) as charge_player_id from player_charge_shop) as charge_player on player.id = charge_player.charge_player_id "
	}

	cStr := strings.Join(channelList, "','")
	whereArray = append(whereArray, fmt.Sprintf(" player.channel in ('%s') ", cStr))

	whereParam := strings.Join(whereArray, " and ")
	if whereParam != "" {
		whereParam = " where " + whereParam
	}

	sql = fmt.Sprintf(
		`SELECT player.id, player.is_online, player_task.task_id, player_task.status FROM (player left join player_task on player.id = player_task.player_id) %s %s `,
		joinStr,
		whereParam)
	r, err := gameDb.GetAll(sql)
	defErr.IfPanic(err, define.DataOptFail)

	var res = make([]*RemainTask, 0, len(r))
	for _, v := range r {
		var RemainTask = &RemainTask{}
		if err = gconv.Struct(v, RemainTask); err != nil {
			defErr.IfPanic(err, define.Fail)
		}
		res = append(res, RemainTask)

	}
	for _, e := range res {
		if e.Status == 2 {
			e.TaskId = 10000
		}
		if r, ok := mapData[e.TaskId]; ok {
			if e.IsOnline == 1 {
				r.Count++
			} else {
				r.Count++
				r.LeaveCount++
			}
		} else {
			if e.IsOnline == 1 {
				mapData[e.TaskId] = &RemainTask{
					TaskId:     e.TaskId,
					Count:      1,
					LeaveCount: 0,
				}
			} else {
				mapData[e.TaskId] = &RemainTask{
					TaskId:     e.TaskId,
					Count:      1,
					LeaveCount: 1,
				}
			}
		}
	}

	totalCreateRole := gameservice.Account.GetTotalCreateRoleCountByChannelList(gs.Node, gs.Sid, channelList)
	var keys []int
	for k, _ := range mapData {
		keys = append(keys, k)
	}
	sort.Ints(keys)
	for _, id := range keys {
		e := mapData[id]
		e.Rate = float32(e.LeaveCount) / float32(totalCreateRole) * 100
		data = append(data, e)
	}
	return data
}

type RemainTime struct {
	StartTime  int     `json:"-"`
	EndTime    int64   `json:"-"`
	TimeString string  `json:"timeString"`
	Count      int     `json:"count"`
	LeaveCount int     `json:"leaveCount"`
	Rate       float32 `json:"rate"`
	IsOnline   int     `json:"isOnline"`
}

// 获取时长分布
func (s *remainService) FetchRemainTime(platformId string, serverId string, channelList []string, startTime int, endTime int, isChargePlayer int) []*RemainTime {
	_, gs := centerservice.CGameServer.GetGameServerList(platformId, []string{serverId}, true)
	gameDb := commondao.DB.NewCustomGdb(gs.Node)
	if gs == nil || gameDb == nil {
		return nil
	}

	var data = []*RemainTime{
		&RemainTime{
			StartTime:  0,
			EndTime:    60,
			TimeString: "小于1分钟",
		},
		&RemainTime{
			StartTime:  60,
			EndTime:    300,
			TimeString: "1~5分钟",
		},
		&RemainTime{
			StartTime:  300,
			EndTime:    600,
			TimeString: "5~10分钟",
		},
		&RemainTime{
			StartTime:  600,
			EndTime:    1800,
			TimeString: "10~30分钟",
		},
		&RemainTime{
			StartTime:  1800,
			EndTime:    3600,
			TimeString: "30~60分钟",
		},
		&RemainTime{
			StartTime:  3600,
			EndTime:    3600 * 2,
			TimeString: "1~2小时",
		},
		&RemainTime{
			StartTime:  3600 * 2,
			EndTime:    3600 * 3,
			TimeString: "2~3小时",
		},
		&RemainTime{
			StartTime:  3600 * 3,
			EndTime:    3600 * 4,
			TimeString: "3~4小时",
		},
		&RemainTime{
			StartTime:  3600 * 4,
			EndTime:    3600 * 5,
			TimeString: "4~5小时",
		},
		&RemainTime{
			StartTime:  3600 * 5,
			EndTime:    3600 * 6,
			TimeString: "5~6小时",
		},
		&RemainTime{
			StartTime:  3600 * 6,
			EndTime:    3600 * 9,
			TimeString: "6~9小时",
		},
		&RemainTime{
			StartTime:  3600 * 9,
			EndTime:    3600 * 12,
			TimeString: "9~12小时",
		},
		&RemainTime{
			StartTime:  3600 * 12,
			EndTime:    3600 * 24,
			TimeString: "12~24小时",
		},
		&RemainTime{
			StartTime:  3600 * 24,
			EndTime:    3600 * 48,
			TimeString: "1~2天",
		},
		&RemainTime{
			StartTime:  3600 * 48,
			EndTime:    3600 * 72,
			TimeString: "2~3天",
		},
		&RemainTime{
			StartTime:  3600 * 72,
			EndTime:    3600 * 999999,
			TimeString: ">3天",
		},
	}

	var (
		// data       = make([]*RemainTask, 0)
		sql        = ""
		joinStr    = ""
		whereArray = make([]string, 0)
	)

	// 0 全部玩家  1 充值过的玩家   2 没充值的玩家
	if isChargePlayer == 1 {
		joinStr = " inner join (select DISTINCT(player_id) as charge_player_id from player_charge_shop) as charge_player on player.id = charge_player.charge_player_id "
	}
	if isChargePlayer == 2 {
		whereArray = append(whereArray, " charge_player.charge_player_id IS NULL")
		joinStr = " left join (select DISTINCT(player_id) as charge_player_id from player_charge_shop) as charge_player on player.id = charge_player.charge_player_id "
	}

	cStr := strings.Join(channelList, "','")
	whereArray = append(whereArray, fmt.Sprintf(" channel in ('%s') ", cStr))

	if startTime > 0 {
		whereArray = append(whereArray, fmt.Sprintf(" reg_time BETWEEN %d AND %d ", startTime, endTime))
	}

	whereParam := strings.Join(whereArray, " and ")
	if whereParam != "" {
		whereParam = " where " + whereParam
	}
	totalCreateRole := gameservice.Account.GetTotalCreateRoleCountByChannelList(gs.Node, serverId, channelList)
	for _, e := range data {

		sql = fmt.Sprintf(
			`SELECT is_online, total_online_time FROM player %s %s and total_online_time >= %d and total_online_time < %d `,
			joinStr,
			whereParam,
			e.StartTime,
			e.EndTime)

		r, err := gameDb.GetAll(sql)
		defErr.IfPanic(err, define.DataOptFail)
		var res = make([]*RemainTime, 0, len(r))
		for _, v := range r {
			var remainTime = &RemainTime{}
			if err = gconv.Struct(v, remainTime); err != nil {
				defErr.IfPanic(err, define.Fail)
			}
			res = append(res, remainTime)
		}
		for _, v := range res {
			e.Count++
			if v.IsOnline == 0 {
				e.LeaveCount++
			}
		}
		if totalCreateRole > 0 {
			e.Rate = float32(e.LeaveCount) / float32(totalCreateRole) * 100
		}

	}

	return data
}
